#include <Core/SearchEngine/MapKeyValueStorage.h>

#include <fstream>
#include <sstream>

namespace Core {

static MapKeyValueStoragePtr defaultKeyValueStorageInst;

void initDefaultMapKeyValueStorage(
	KeyValueStorageParams const & params, boost::filesystem::path const & path) 
{
	if (!defaultKeyValueStorageInst)
		defaultKeyValueStorageInst.reset(new MapKeyValueStorage(params, path));
}

MapKeyValueStoragePtr defaultMapKeyValueStorade() 
{
	return defaultKeyValueStorageInst;
}

/**
 * Helpers
 */
void ParseSignatureStorage(
	boost::unordered_map<std::string, KeyBucket> & signatureStrorage, std::vector<std::string>& keys,
	boost::filesystem::path const & path);

/**
 * Public MapKeyValueStorage's api
 */
MapKeyValueStorage::MapKeyValueStorage(KeyValueStorageParams const & params, boost::filesystem::path const & path) 
	: IKeyValueStorage(params), m_baseParams(params), m_path(path) 
{ 
}

MapKeyValueStorage::~MapKeyValueStorage() 
{
}

void MapKeyValueStorage::initialize() throw (std::exception) 
{
	loadStorage(m_path);
	setKeyMaxLength();
}

void MapKeyValueStorage::insert(std::string const & key, KeyBucket const & bucket) 
{
	// TODO impl this
}	

void MapKeyValueStorage::remove(std::string const & key) 
{
	// TODO impl this
}

void MapKeyValueStorage::getKey(std::string & key, std::size_t index)
{
	key.assign(keys.at(index));
}

std::size_t MapKeyValueStorage::getKeyCount()
{ 
	return keys.size(); 
}

void MapKeyValueStorage::getNextKey(std::string & key)
{
}

bool MapKeyValueStorage::getBucket(std::string const & key, KeyBucket & bucket)
{
	bool res = false;
	boost::unordered_map<std::string, KeyBucket>::const_iterator it = m_signStorage.find(key);
	if (it != m_signStorage.end()) {
		bucket.GUID.assign(it->second.GUID);
		res = true;
	}

	return res;
}

bool MapKeyValueStorage::containsBucket(std::string const & key)
{
	bool res = false;
	boost::unordered_map<std::string, KeyBucket>::const_iterator it = m_signStorage.find(key);
	if (it != m_signStorage.end())
		res = false;
	return res;
};

/**
 * Private MapKeyValueStorage's api
 */
void MapKeyValueStorage::loadStorage(boost::filesystem::path const & path)
{
	ParseSignatureStorage( m_signStorage, keys, path );
}

void MapKeyValueStorage::setKeyMaxLength()
{
	unsigned int maxSize = 0;
	boost::unordered_map<std::string, KeyBucket>::const_iterator it = m_signStorage.begin();
	for(; it != m_signStorage.end(); ++it) {
		size_t size = it->first.size();
		if ( size > maxSize )
			maxSize = size;
	}
	m_keyMaxLength = maxSize;
}

void strToHexStr(std::string& dest, std::string const & source)
{
	const char* hexStr = source.c_str();
	int length = strlen( hexStr ) >> 1;
	dest.reserve( length );
	for ( int i = 0; i < length << 1; i += 2 )
    {
		std::stringstream ss;
		ss << hexStr[ i ] << hexStr[ i + 1 ];
		ss << std::hex;
		int ch;
		ss >> ch;
		dest.push_back( ch );
	}
}

void readNextSign(std::string& source, std::string& key)
{
	size_t pos = source.find_first_of(".");
	strToHexStr(key, source.substr( 0, pos ));
}

void readNextGUID(std::string& source, KeyBucket& kb)
{
	size_t pos = source.find_first_of( "." );
	kb.GUID.assign( source.substr( pos + 1 ) );
}

std::pair<std::string, KeyBucket> GetNextMalwarePair(std::ifstream & in)
{
	std::string nextStr, key;
	in >> nextStr;
	readNextSign(nextStr, key);
	KeyBucket keyBucket;
	readNextGUID(nextStr, keyBucket);
	return std::make_pair(key, keyBucket);
}

bool FillSignatureMap(
	boost::unordered_map<std::string, KeyBucket> & signatureStrorage, std::vector<std::string>& keys,
	boost::filesystem::path const & path)
{ 
	bool res = true;
	std::ifstream in(path.generic_string().c_str());
	if (in && in.is_open()) {
		while (!in.eof()){
			std::pair<std::string, KeyBucket> pair(GetNextMalwarePair(in));
			if (!pair.first.empty())
			{
				signatureStrorage.insert(pair);
				keys.push_back( pair.first );
			}
		}
		return true;
	}
	return false;
}

void ParseSignatureStorage(
	boost::unordered_map<std::string, KeyBucket> & signatureStrorage, std::vector<std::string>& keys,
	boost::filesystem::path const & path)
{
	try
	{
		if (!boost::filesystem::is_directory(path) && !boost::filesystem::is_empty(path)) 
			FillSignatureMap( signatureStrorage, keys, path );
	}
	catch ( const boost::filesystem::filesystem_error & ) 
	{
	}
}

} // namespace Core

